<template>
  <!-- #ifndef APP-NVUE -->
  <scroll-view
    class="scroyy"
    :scroll-top="scrollTop"
    @touchstart="onTouchStart"
    @touchmove="onTouchMove"
    @touchend="onTouchEnd"
    @scroll="onScroll"
    @scrolltoupper="onScrollToTop"
    @scrolltolower="onScrollToBottom"
    scroll-y
    :enable-flex="true"
    enable-back-to-top
    enhanced
    scroll-with-animation
    :bounces="false"
    :style="[props.height ? { height: props.height + 'rpx' } : '', _style]"
    :class="_class"
  >
    <view
      :class="['scroyy__track', 'scroyy__track--' + (loosing ? 'loosing' : '')]"
      :style="{ transform: `translate3d(0, ${_barHeight}rpx, 0)` }"
    >
      <view
        class="scroyy__tips"
        :class="['scroyy__track--' + (loosing ? 'loosing' : '')]"
        :style="{ height: _barHeight + 'rpx' }"
      >
        <slot name="pull" :status="{ refreshStatus }">
          <view v-if="refreshStatus === 2" class="flex flex-row flex-row-center-center">
            <tm-icon :font-size="24" color="primary" name="tmicon-shuaxin" spin></tm-icon>
            <tm-text color="grey" _class="pl-16" :label="loadingTexts[refreshStatus]"></tm-text>
          </view>
          <view
            v-if="refreshStatus != -1 && refreshStatus != 2"
            class="flex flex-row flex-row-center-center srrryration"
            :style="{
              opacity: `${refreshStatus == 0 ? _barHeight / props.loadBarHeight : 1}`,
            }"
          >
            <view
              :class="
                refreshStatus == 0 ? 'srrryration srrryrationOn' : 'srrryration srrryrationOf'
              "
            >
              <tm-icon :font-size="24" color="primary" name="tmicon-long-arrow-down"></tm-icon>
            </view>
            <tm-text color="grey" _class="pl-16" :label="loadingTexts[refreshStatus]"></tm-text>
          </view>
        </slot>
      </view>
      <slot></slot>
      <view
        :class="['scroyy__track--loosing  ']"
        :style="{ height: (isBootRefresh ? props.loadBarHeight : 0) + 'rpx' }"
      >
        <slot name="bottom" :status="{ isBootRefresh }">
          <view
            v-if="isBootRefresh"
            class="flex flex-row flex-row-center-center"
            :style="{ height: (isBootRefresh ? props.loadBarHeight : 0) + 'rpx' }"
          >
            <tm-icon :font-size="24" color="primary" name="tmicon-shuaxin" spin></tm-icon>
            <tm-text color="grey" _class="pl-16" label="数据加载中"></tm-text>
          </view>
        </slot>
      </view>
    </view>
  </scroll-view>
  <!-- #endif -->
  <!-- #ifdef APP-NVUE -->
  <scroll-view
    enableBackToTop="true"
    alwaysScrollableVertical="true"
    scroll-y="true"
    @scrolltolower="onScrollToBottom"
    :style="[props.height ? { height: props.height + 'rpx' } : '', _style]"
    :class="_class"
  >
    <refresh
      @refresh="onrefresh"
      @pullingdown="onpullingdown"
      :display="showLoading ? 'show' : 'hide'"
      style="width: 750rpx"
    >
      <view :style="{ height: _barHeight / 2 + 'px' }">
        <slot name="pull" :status="{ refreshStatus }">
          <view
            class="flex flex-row flex-row-center-center"
            :style="{
              height: _barHeight / 2 + 'px',
              opacity: refreshStatus == 2 || refreshStatus == 3 ? 1 : 0,
            }"
          >
            <tm-icon :font-size="24" color="primary" name="tmicon-shuaxin" spin></tm-icon>
            <tm-text color="grey" _class="pl-16" :label="loadingTexts[refreshStatus]"></tm-text>
          </view>
          <view
            v-if="refreshStatus == 0 || refreshStatus == 1"
            class="flex flex-row flex-row-center-center srrryration"
            :style="{
              opacity: `${refreshStatus == 0 ? _barHeight / nowEvt.viewHeight : 1}`,
              height: _barHeight / 2 + 'px',
              top: -(_barHeight / 2) + 'px',
            }"
          >
            <view
              :class="
                refreshStatus == 0 ? 'srrryration srrryrationOn' : 'srrryration srrryrationOf'
              "
            >
              <tm-icon :font-size="24" color="primary" name="tmicon-long-arrow-down"></tm-icon>
            </view>
            <tm-text color="grey" _class="pl-16" :label="loadingTexts[refreshStatus]"></tm-text>
          </view>
        </slot>
      </view>
    </refresh>
    <slot></slot>
  </scroll-view>
  <!-- #endif -->
</template>
<!-- #ifndef APP-NVUE -->
<script lang="ts" setup>
  import tmIcon from '../tm-icon/tm-icon.vue';
  import tmText from '../tm-text/tm-text.vue';
  import { getCurrentInstance, nextTick, onMounted, ref, Ref, watch, computed } from 'vue';
  import { propsdetail } from './propsdetail';
  import { debounce } from 'lodash-es';
  const proxy = getCurrentInstance()?.proxy ?? null;
  const emits = defineEmits([
    'bottom',
    'change',
    'refresh',
    'timeout',
    'update:modelValue',
    'update:bottomValue',
  ]);
  const props = defineProps({
    ...propsdetail,
  });
  // 下拉开始的起点，主要用于计算下拉高度
  const startPoint: Ref<{
    pageX: number;
    pageY: number;
  } | null> = ref(null);
  const isPulling = ref(false); // 是否下拉中
  const _maxBarHeight = ref(props.maxBarHeight); // 最大下拉高度，单位 rpx
  const _refresher = computed(() => props.refresher);
  // 触发刷新的下拉高度，单位rpx
  // 松开时下拉高度大于这个值即会触发刷新，触发刷新后松开，会恢复到这个高度并保持，直到刷新结束
  const _barHeight = ref(0);
  /** 开始刷新 - 刷新成功/失败 最大间隔时间setTimeout句柄 */
  let maxRefreshAnimateTimeFlag: any | null = 0;
  /** 关闭动画耗时setTimeout句柄 */
  let closingAnimateTimeFlag: any | null = 0;
  //加载框的高度
  const refreshStatus = ref(-1);
  const loosing = ref(false);
  const enableToRefresh = ref(true);
  const scrollTop = ref(0);
  const oldScollTop = ref(0);
  const loadingTexts = computed(() => props.loadingTexts);
  /** 触底下拉刷新参数。 */
  const isBootRefresh = ref(props.bottomValue);
  const _class = computed(() => props._class);
  const _style = computed(() => props._style);
  watch(
    () => props.modelValue,
    () => {
      if (!props.modelValue) {
        if (maxRefreshAnimateTimeFlag != null) {
          clearTimeout(maxRefreshAnimateTimeFlag);
        }
        refreshStatus.value = 3;
        close();
      }
    },
  );
  watch(
    () => props.bottomValue,
    () => {
      isBootRefresh.value = props.bottomValue;
    },
  );
  onMounted(() => {
    clearTimeout(maxRefreshAnimateTimeFlag);
    clearTimeout(closingAnimateTimeFlag);
    nextTick(() => setDefault());
  });

  function setDefault() {
    if (props.defaultValue) {
      setRefreshBarHeight(props.loadBarHeight);
      refreshStatus.value = 2;
      loosing.value = true;
      isPulling.value = true;
      enableToRefresh.value = false;
      startPoint.value = null;
    }
  }

  function onScrollToBottom() {
    if (isBootRefresh.value) return;
    emits('update:bottomValue');
    emits('bottom');
  }

  function onScrollToTop() {
    enableToRefresh.value = true;
  }

  function onScroll(e: any) {
    // 新增的
    oldScollTop.value = e.detail.scrollTop;
    enableToRefresh.value = e.detail?.scrollTop === 0;
  }

  function setScrollTop(tp: number) {
    scrollTop.value = tp;
  }

  function scrollToTop() {
    // 新增 设置为之前的
    scrollTop.value = oldScollTop.value;
    nextTick(() => {
      setScrollTop(0);
    });
  }

  defineExpose({
    scrollToTop,
  });

  function onTouchStart(e: TouchEvent) {
    if (isPulling.value || !enableToRefresh.value || !_refresher.value) return;
    const { touches } = e;
    if (touches.length !== 1) return;
    const { pageX, pageY } = touches[0];

    loosing.value = false;
    startPoint.value = {
      pageX,
      pageY,
    };
    isPulling.value = true;
  }

  function onTouchMove(e: TouchEvent) {
    if (!startPoint.value || !_refresher.value) return;
    const { touches } = e;

    if (touches.length !== 1) return;

    const { pageY } = touches[0];
    const offset = pageY - startPoint.value.pageY;
    const barsHeight = uni.$tm.u.torpx(offset);

    if (barsHeight > 0) {
      if (barsHeight > _maxBarHeight.value) {
        // 限高
        setRefreshBarHeight(_maxBarHeight.value);
        // this.startPoint.pageY = pageY - this.toPx(this.maxBarHeight); // 限高的同时修正起点，避免触摸点上移时无效果
      } else {
        setRefreshBarHeight(barsHeight);
      }
    }
  }

  function onTouchEnd(e: TouchEvent) {
    if (!startPoint.value || !_refresher.value) return;
    const { changedTouches } = e;
    if (changedTouches.length !== 1) return;
    const { pageY } = changedTouches[0];
    const barsHeight = uni.$tm.u.torpx(pageY - startPoint.value.pageY);
    startPoint.value = null; // 清掉起点，之后将忽略touchMove、touchEnd事件

    loosing.value = true;
    isBootRefresh.value = false;
    // 松开时高度超过阈值则触发刷新
    if (barsHeight > props.loadBarHeight) {
      _barHeight.value = props.loadBarHeight;
      refreshStatus.value = 2;

      emits('change', true);
      emits('update:modelValue', true);
      emits('refresh');
      maxRefreshAnimateTimeFlag = setTimeout(() => {
        maxRefreshAnimateTimeFlag = null;

        if (refreshStatus.value === 2) {
          // 超时回调
          emits('timeout');
          close(); // 超时仍未被回调，则直接结束下拉
        }
      }, props.refreshTimeout as any) as any as number;
    } else {
      close();
    }
  }

  function setRefreshBarHeight(barsHeight: number) {
    if (!_refresher.value) return;
    if (barsHeight >= props.loadBarHeight) {
      refreshStatus.value = 1;
    } else {
      refreshStatus.value = 0;
    }
    return new Promise((resolve) => {
      _barHeight.value = barsHeight;
      nextTick(() => {
        resolve(barsHeight);
      });
    });
  }

  function close() {
    const animationDuration = 350;
    _barHeight.value = 0;
    emits('change', false);
    emits('update:modelValue', false);
    closingAnimateTimeFlag = setTimeout(() => {
      closingAnimateTimeFlag = null;
      refreshStatus.value = -1;
      isPulling.value = false; // 退出下拉状态
      loosing.value = false;
      enableToRefresh.value = true;
    }, animationDuration) as any as number;
  }
</script>

<style scoped>
  .scroyy {
    overflow: hidden;
    /* #ifndef APP-NVUE */
    max-height: 100%;
    /* #endif */
  }

  .scroyy__track {
    position: relative;
  }

  .scroyy__track--loosing {
    transition-property: transform, height, opacity;
    transition-timing-function: ease;
    transition-duration: 0.35s;
  }

  .scroyy__tips {
    position: absolute;
    color: #bbb;
    font-size: 24rpx;
    top: 0;
    width: 100%;
    transform: translateY(-100%);
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    overflow: hidden;
  }

  .scroyy__text {
    margin: 16rpx 0 0;
  }

  .scroyy__wrap {
    position: relative;
  }
</style>
<!-- #endif -->

<!-- #ifdef APP-NVUE -->

<script lang="ts" setup>
  import tmIcon from '../tm-icon/tm-icon.vue';
  import tmText from '../tm-text/tm-text.vue';
  import { getCurrentInstance, nextTick, onMounted, ref, Ref, watch } from 'vue';
  import { propsdetail } from './propsdetail';
  const proxy = getCurrentInstance()?.proxy ?? null;
  const emits = defineEmits([
    'bottom',
    'change',
    'refresh',
    'timeout',
    'update:modelValue',
    'update:bottomValue',
  ]);
  const props = defineProps({
    ...propsdetail,
  });
  interface pullevent {
    dy: number;
    pullingDistance: number;
    viewHeight: number;
    type: string;
  }

  const showLoading = ref(false);
  const refreshing = ref(false);
  const isBootRefresh = ref(false);
  const refreshStatus = ref(-1);
  /** 开始刷新 - 刷新成功/失败 最大间隔时间setTimeout句柄 */
  let maxRefreshAnimateTimeFlag: number | null = 0;
  const _barHeight = ref(uni.$tm.u.topx(props.loadBarHeight));
  const nowEvt: Ref<pullevent> = ref({
    dy: 0,
    pullingDistance: 0,
    viewHeight: 0,
    type: '',
  });

  onMounted(() => {
    if (maxRefreshAnimateTimeFlag != null) {
      clearTimeout(maxRefreshAnimateTimeFlag);
    }
  });
  watch(
    () => props.modelValue,
    () => {
      if (!props.modelValue) {
        if (maxRefreshAnimateTimeFlag != null) {
          clearTimeout(maxRefreshAnimateTimeFlag);
        }
        close();
      }
    },
  );
  watch(
    () => props.bottomValue,
    () => {
      isBootRefresh.value = props.bottomValue;
    },
  );
  function onScrollToBottom() {
    if (isBootRefresh.value) return;
    emits('update:bottomValue', true);
    isBootRefresh.value = true;
    emits('bottom');
  }
  function onrefresh() {
    if (nowEvt.value.pullingDistance >= _barHeight.value) {
      refreshStatus.value = 2;
      showLoading.value = true;
      emits('refresh');
      emits('change', true);
      emits('update:modelValue', true);
      maxRefreshAnimateTimeFlag = setTimeout(() => {
        maxRefreshAnimateTimeFlag = null;
        if (refreshStatus.value === 2) {
          // 超时回调
          emits('timeout');
          close(); // 超时仍未被回调，则直接结束下拉
        }
      }, props.refreshTimeout as any) as any as number;

      return;
    }
    showLoading.value = true;
    nextTick(() => {
      showLoading.value = false;
      emits('change', false);
    });
  }
  /**
	 * dy: 前后两次回调滑动距离的差值
	pullingDistance: 下拉的距离
	viewHeight: refresh 组件高度
	type: “pullingdown” 常数字符串
	 */
  function onpullingdown(evt: {
    dy: number;
    pullingDistance: number;
    viewHeight: number;
    type: string;
  }) {
    evt.pullingDistance = Math.abs(evt.pullingDistance);
    nowEvt.value = evt;
    if (evt.pullingDistance <= 1) {
      refreshStatus.value = -1;
    } else if (evt.pullingDistance > 1 && evt.pullingDistance < _barHeight.value) {
      refreshStatus.value = 0;
    } else if (evt.pullingDistance >= _barHeight.value) {
      refreshStatus.value = 1;
    }
  }

  function close() {
    showLoading.value = false;
    refreshStatus.value = -1;
    emits('change', false);
    emits('update:modelValue', false);
  }
</script>

<!-- #endif -->

<style>
  .srrryration {
    transition-property: transform, height, opacity;
    transition-timing-function: ease;
    transition-duration: 0.25s;
  }

  .srrryrationOn {
    transform: rotate(0deg);
  }

  .srrryrationOf {
    transform: rotate(180deg);
  }
</style>
